#include "flexical.hpp"
#include "ftool.hpp"

//将尾巴和手指后移，定位下一个字符
#define MAKE_TAIL \
    tail++; \
    p++;

//向后取字符，直到不符合规范，然后拷贝这个
#define MAKE_WORD(FUN_NAME) \
    while(FUN_NAME(*(p))) { \
        MAKE_TAIL \
    } \
    memcpy(tempStr, &word[head], tail); \
    tempStr[tail] = '\0';

#define RE_TAIL \
    head = head + tail; \
    tail = 0;

fairy::FLexical::FLexical(const char * filename)
{
    if(!fairy.open(filename,Ffile::binary|Ffile::in))
    {
        std::cout << "朕打不开 " << filename << " 文件啊 ┑(￣Д ￣)┍" << std::endl;
        exit(0);
    }

}

auto fairy::FLexical::analysis() -> int
{
    while(!fairy.get_value().eof())
    {
        //小手指，标记现在指的是那个字符
        char *p = word;
        /*
         *  小脑袋，将标记一个符合标准的字符的起始位置
         *  一个标准的字符总是以小脑袋开始，并且以小尾巴结束  
         */
        int head = 0;
        /*
         *  小尾巴，标记一个符合规则的单词应该被截取到的位置
         *  在获取一个符合标准的首字符后，将会继续向下寻去，直到不符合要求，这个小尾巴将跟随前进
         *  在取到第一个不符合要求的字符时，小尾巴停止，将字符串取到这个位置
         */
        int tail = 0;
        /*
         *  小口袋，用来临时装一下字符串
         *  在手指（p)确定一个字符串的尾部时，将由小口袋临时保存这个字符串
         * 
         */
        char tempStr[SINGLE_WORDS_NUM] = {0};

        strncpy(word,fairy.read_word().c_str(),WORD_MAX_SIZE);
        auto is_label = [](char _value){return (_value == '_' || isdigit(_value) || isalpha(_value)) ? true : false;};
        while(*p){

            //保留字与标识符判别
            if(*p == '_' || isalpha(*p)){
                MAKE_WORD(is_label)
                if(is_word(tempStr))
                    lexicaldata.push_back(std::make_pair(tempStr,5));
                else
                    lexicaldata.push_back(std::make_pair(tempStr,1));
                RE_TAIL
            }
            // 数字常量的识别
            else if(isdigit(*p)){       
                MAKE_WORD(isdigit)
                lexicaldata.push_back(std::make_pair(tempStr,2));
                //同上
                RE_TAIL
            }
            //运算符识别
            else if(is_alone_count(*p)) {
                MAKE_WORD(is_alone_count)
                if(is_count(tempStr))
                    lexicaldata.push_back(std::make_pair(tempStr,3));
                    RE_TAIL
            }
            //分隔符识别
            else if(is_border(*p)){
                memcpy(tempStr, &word[head], 1);    //手动将字符串放进口袋中
                tempStr[1] = '\0';                  //手动拉上口袋的拉链
                lexicaldata.push_back(std::make_pair(tempStr,4));
                MAKE_TAIL
                RE_TAIL
            }
            //这个地方要详细备注,一定要备注
            else if(*p == '\"') {
                //临时储存字符串的小口袋
                char str[STRING_MAX_NUM];
                //跳过第一个双引号
                MAKE_TAIL
                /*
                 *  进入上一次读取的词的循环
                 *  这里有个很重要的判断，如果一个字符串常量是在一个词中直接结束的（像这样：”fsdfsf"）
                 *      则使用getline会出现问题，getline去除的串是在这个循环所用的词之后开始取的，所以不会检测到当前这个串的第二个双引号
                 */
                while(*p && *p != '\"') { tail++; p++; }
                /*
                 * 这里判断一个字符串常量是否为一个词包含的
                 * 在上面的循环结束后，检测*p是以哪个条件结束循环的：
                 *  如果是*p指向了空，则说明这个字符串常量不是存在于一个词中
                 *  如果是以*p=='\"'结束的，则说明这个常量是存在于一个词中的
                 */
                if(*p == '\"') { 
                    memcpy(str,&word[head+1],tail-1);    //直接将两个双引号之间的内容拷贝到小口袋中
                    str[tail-1] = '\0';                  //拉上拉链
                    lexicaldata.push_back(std::make_pair(str,2));
                    MAKE_TAIL
                    RE_TAIL
                }
                //这个情况就是字符串常量并不是存在于一个词中的
                else{
                    //现将这次循环中使用的单词的剩余部分装进小口袋中
                    memcpy(str,&word[head+1],tail-1);
                    //在继续向后取，知道遇见下一个双引号,将取到的所有内容继续装进小口袋
                    fairy.get_value().getline(&str[tail-1], STRING_MAX_NUM,'\"');
                    lexicaldata.push_back(std::make_pair(str,2));
                    /*
                     *  这个break比较核心，它在取到结尾双引号结束时，跳出了这个循环。进入下一次取词状态
                     *  而下一次取词状态并不是从一个单词的首部开始取的，而是接着双引号。将双引号后的词带入循环中继续判断
                     */
                    break;
                }
            }
            else break;
        }
    }
    //正确返回0,其余全部是错误的标志
    return 0;
}

auto fairy::FLexical::display_data() -> void 
{
    LD::iterator begin = lexicaldata.begin();
    for(LD::iterator iter = begin; iter != lexicaldata.end(); iter++)
    {
        std::cout << iter->first << " \t\tis-------> " << iter->second << std::endl;
    } 
}